package Commons.Logging;

import Commons.WebServiceClient.RegWebService;
import Commons.WebServiceClient.WebServiceClient;
import Commons.WriterXML;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import javax.xml.stream.XMLStreamException;

/**
 *
 * @author Team5
 */
public class Logger implements Runnable{

    private File filelog;
    private Log log;
    private boolean filevuoto=true;
    private Thread scritturaLocale;
    private Thread registrazioneRemota;
    private HashMap<Integer,Integer> vectorClock;
    private int posizioneEntitaNelVectorClock;
    private WriterXML wXml;
    private String tipoEntita;
    private int numerooperazione;

    /**
     * Costruttore di default.
     */
    public Logger() {
        numerooperazione=0;
        this.tipoEntita="sconosciuta";
        this.posizioneEntitaNelVectorClock=-1;
        this.vectorClock= new HashMap<Integer,Integer>();
        wXml = new WriterXML("/home/carlo/Scrivania/test2.xml");
        wXml.startXML();
        scritturaLocale = new Thread(this);
        registrazioneRemota=new Thread(new RegWebService(this.tipoEntita,this));
        this.registrazioneRemota.start();
   }

    /**
     * 
     * @param pathFileXML
     * @param tipoEntita
     */
    public Logger(String pathFileXML,String tipoEntita) {
        numerooperazione=0;
       //vc = new VectorClock();
       this.tipoEntita=tipoEntita;
       this.posizioneEntitaNelVectorClock=-1;
       this.vectorClock= new HashMap<Integer,Integer>();
       wXml=new WriterXML(pathFileXML);
       wXml.startXML();
       scritturaLocale = new Thread(this);
       registrazioneRemota=new Thread(new RegWebService(this.tipoEntita,this));
       this.registrazioneRemota.start();
    }
    public Logger(String pathFileXML,String tipoEntita, HashMap<Integer,Integer> vc, int posizioneNelVC) {
        numerooperazione=0;
       //vc = new VectorClock();
       this.tipoEntita=tipoEntita;
       this.posizioneEntitaNelVectorClock=posizioneNelVC;
       this.vectorClock= new HashMap<Integer,Integer>();
       this.vectorClock= vc;
       wXml=new WriterXML(pathFileXML);
       wXml.startXML();
       scritturaLocale = new Thread(this);
       registrazioneRemota=new Thread(new RegWebService(this.tipoEntita,this));
       
    }
   
    /*Metodo di logging: riceve la stringa di log ed esegue un thread per la sua memorizzazione sul file locale dei log ed un thread per la comunicazione remota del log al monitor.
     *
     */
    public void logging(Log log) throws InterruptedException{
    //esegue un thread per la scrittura del log sul file e un altro thread per la comunicazione del log al monitor globale
        
        //prima di lanciare l'invio remoto di un nuovo log attende che sia stata eseguita la registrazione al WS
        this.registrazioneRemota.join();
        this.vectorClock.put(posizioneEntitaNelVectorClock, this.vectorClock.get(this.posizioneEntitaNelVectorClock) + 1);
        log.setVectorClock(this.vectorClock);
        this.log=log;
        //this.log.setNumerooperazione(this.vectorClock.get(posizioneEntitaNelVectorClock));
        System.out.println("LOG "+this.vectorClock.get(posizioneEntitaNelVectorClock));

        numerooperazione++;
        //System.out.println("Logger: il mio numerooperazione "+numerooperazione);
        //System.out.println("Adesso passo il numero al log e setto");
        log.setNumerooperazione(numerooperazione);


        WebServiceClient cws = new WebServiceClient(log);
        Thread scritturaRemota = new Thread(cws);
        scritturaRemota.start();
        this.scritturaLocale.join();
        scritturaLocale = new Thread(this);
        //start thread per la scrittura locale
        scritturaLocale.start();
        }

    /**Questo metodo serve per garantire una corretta chiusura del file xml dei log.
      * Prima di inserire l'ultimo tag di chiusura la funzione chiama un join sul thread che si occupa della scrittura dei log, in modo da gestire la concorrenza nella gestione della scrittura sul file e garantire che il tag di chiusura sia posizionato alla fine del file.
      *
      * @throws IOException
      * @throws XMLStreamException
      * @throws InterruptedException
      */
    public void chiudiFileLog() throws IOException, XMLStreamException, InterruptedException{
            //chiamo join sul thread che si occupa della scrittura dei log.
            this.scritturaLocale.join();
            wXml.closeXML();
            /*
            FileWriter file= new FileWriter(this.filelog, true);
            XMLOutputFactory f;
            XMLStreamWriter w;
            f = XMLOutputFactory.newInstance();
            w = f.createXMLStreamWriter(file);
            w.writeEmptyElement("/logs>");
            w.close();*/
    }
    /**
     * Thread per la scrttura locale del log
     */
    public void run() {
        wXml.setContatore(this.vectorClock.get(posizioneEntitaNelVectorClock));
        wXml.writeLogXML(log);

    }

    public void setPosizione(int posizione){
        this.posizioneEntitaNelVectorClock=posizione;
        this.vectorClock.put(posizione, 0);
    }

    public HashMap<Integer,Integer> getVectorClock(){
        return this.vectorClock;
    }

    /**
     * Operazione di merge di vectorclock. Aggiorna il vectorclock corrente
     * mediante il merge con il vectorclock passato come parametro.
     *
     * @param vc Il vectorclock da fondere con quello corrente.
     */
    public void merge(HashMap<Integer, Integer> vc) {
        // Ciclo su tutti gli elementi del vectorclock ricevuto come parametro
        for (Integer posizione : vc.keySet())
            // Inserisco il valore letto
            // se non è già presente nel mio vectorclock
            // oppure se esso è maggiore di quello già presente,
            // ad eccezione del valore relativo alla mia entità
            if ((!this.getVectorClock().containsKey(posizione) || this.getVectorClock().get(posizione) < vc.get(posizione)) && posizione!=this.posizioneEntitaNelVectorClock)
                this.getVectorClock().put(posizione, vc.get(posizione));
    }
    public int getPosizioneNelVC(){
        return this.posizioneEntitaNelVectorClock;
    }
}
